//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
//-----------------------------------------------------------------------------
// Net ARP.C
//
// This module handles ARP(Address Resolution Protocol) messages and ARP resolution and manages
// the ARP cache. Refer to RFC 826 and RFC 1122
//-----------------------------------------------------------------------------
#include "lh7a400_map.h"
#include "LH7A400_drivers.h"
#include "watertek_drivers.h"
#include "net_net.h"
#include "watertek_main.h"

extern WAIT   wait1;
extern UNS_8  my_hwaddr[];
extern UNS_8  broadcast_hwaddr[];
extern UNS_32 my_ipaddr;
extern UNS_8  Dm_arpBuf[];

//lcd
extern UNS_16 lcdCurX;
extern UNS_16 lcdCurY;

ARP_CACHE  arp_cache[CACHESIZE];
UNS_8 waiting_for_arp;

void init_arp(void)
{
    memset(arp_cache, 0, sizeof(arp_cache));
    memset(&wait1, 0, sizeof(wait1));
    waiting_for_arp = FALSE;
}

//------------------------------------------------------------------------
// This is called every 60 seconds to age the ARP cache
// If an entry times out then it is deleted from the cache
// See "TCP/IP Illustrated, Volume 1" Sect 4.3
//------------------------------------------------------------------------
void age_arp_cache(void)
{
    UNS_8 i;

    for (i = 0; i < CACHESIZE; i++) {
        if ((arp_cache[i].ipaddr != 0) && (arp_cache[i].timer)) {
            arp_cache[i].timer--;
            if (arp_cache[i].timer == 0) {
                // Timed out so clear out cache entry
                // Do not need to zero hwaddr
                arp_cache[i].ipaddr = 0;
            }
        }
    }
}

//------------------------------------------------------------------------
// This allocates memory for the entire outgoing message,
// including eth and ip headers, then builds an outgoing
// ARP response message
// See "TCP/IP Illustrated, Volume 1" Sect 4.4
//------------------------------------------------------------------------
void arp_send(UNS_8 *hwaddr, UNS_32 ipaddr, UNS_16 msg_type)
{
    ARP_HEADER  *arp;
    UNS_32 i;

    arp = (ARP_HEADER  *)(Dm_arpBuf + ETH_HEADER_LEN);
    arp->hardware_type = DIX_ETHERNET;
    arp->protocol_type = IP_PACKET;
    arp->hwaddr_len = 6;
    arp->ipaddr_len = 4;
    arp->message_type = msg_type;

    // My hardware address and IP addresses
    memcpy(arp->source_hwaddr, my_hwaddr, 6);
    arp->source_ipaddr = my_ipaddr;

    // Destination hwaddr and dest IP addr
    if (msg_type == ARP_REQUEST) {
        memset(arp->dest_hwaddr, 0, 6);
    }
    else {
        memcpy(arp->dest_hwaddr, hwaddr, 6);
    }

    arp->dest_ipaddr = ipaddr;

    // If request then the message is a brodcast, if a response then
    // send to specified hwaddr
    // ARP payload size is always 28 bytes
    if (msg_type == ARP_REQUEST) {
        ETH_Send(Dm_arpBuf, broadcast_hwaddr, ARP_PACKET, ARP_HEADER_LEN);
    }
    else {
        ETH_Send(Dm_arpBuf, hwaddr, ARP_PACKET, ARP_HEADER_LEN);
    }
}

//------------------------------------------------------------------------
// This re-sends an ARP request if there was no response to
// the first one.  It is called every 0.5 seconds.  If there
// is no response after 2 re-tries, the datagram that IP was
// trying to send is deleted
//-----------------------------------------------------------------------
//static
UNS_8  retries = 0;
void arp_retransmit(void)
{

    if ((waiting_for_arp) && (wait1.timer)) {
        wait1.timer--;
        if (wait1.timer == 0) {
            retries++;
            if (retries <= 2) {
                DBGMSG("ARP : Resend requese !        " );
                arp_send(NULL, wait1.ipaddr, ARP_REQUEST);
                wait1.timer = ARP_TIMEOUT;
            }
            else {
                wait1.timer = 0;
                waiting_for_arp = 0;
                retries = 0;
            }
        }
    }
}

//------------------------------------------------------------------------
// Find the ethernet hardware address for the given ip address
// If destination IP is on my subnet then we want the eth
// address of destination, otherwise we want eth addr of gateway.
// Look in ARP cache first.  If not found there, send ARP request.
// Return pointer to the hardware address or NULL if not found
// See "TCP/IP Illustrated, Volume 1" Sect 4.5
//------------------------------------------------------------------------
UNS_8  *arp_resolve(UNS_32 dest_ipaddr)
{
    UNS_8 i;

    // See if IP addr of interest is in ARP cache
    for (i = 0; i < CACHESIZE; i++) {
        if (arp_cache[i].ipaddr == dest_ipaddr) {
            return (&arp_cache[i].hwaddr[0]);
        }
    }

    DBGMSG("ARP : Send requese !          " );

    // Not in cache so broadcast ARP request
    arp_send(NULL, dest_ipaddr, ARP_REQUEST);

    // Set a flag to indicate that an IP datagram is waiting
    // to be sent
    waiting_for_arp = TRUE;

    // Null means that we have sent an ARP request
    return (NULL);
}

//------------------------------------------------------------------------
// This handles incoming ARP messages
// See "TCP/IP Illustrated, Volume 1" Sect 4.4
// Todo:  Resolve problem of trying to add to a full cache
//------------------------------------------------------------------------
void arp_rcve(UNS_8 *inbuf)
{
    UNS_8  i, cached, oldest;
    UNS_16  minimum;
    ARP_HEADER  *arp;

    arp = (ARP_HEADER  *)(inbuf + ETH_HEADER_LEN);
    cached = FALSE;

    if (arp->message_type == ARP_REQUEST) {
        DBGMSG("ARP : Request rcvd !          " );
    }
    else {
        DBGMSG("ARP : Response rcvd  !        " );
    }

    // Validate incoming frame
    if ((arp->hardware_type != DIX_ETHERNET) || (arp->protocol_type != IP_PACKET)) {
        DBGMSG("ARP : HW or PROTYPE not match!" );
        return;
    }

    // Search ARP cache for senders IP address
    // If found, update entry and restart timer
    for (i = 0; i < CACHESIZE; i++) {
        if (arp_cache[i].ipaddr == arp->source_ipaddr) {
            memcpy(&arp_cache[i].hwaddr[0], &arp->source_hwaddr[0], 6);
            arp_cache[i].timer = CACHETIME;
            cached = TRUE;
            DBGMSG("ARP : Cache entry updated !   " );
            break;
        }
    }

    if (arp->dest_ipaddr != my_ipaddr) {
        DBGMSG("ARP : It's not my ipaddr !    " );
        return;
    }

    // At this point we know the the frame is addressed to me
    // If not already in cache then add entry and start timer
    if (cached == FALSE) {
        // Find first blank space and add entry
        // Blank entries are indicated by ip addr = 0
        for (i = 0; i < CACHESIZE; i++) {
            if (arp_cache[i].ipaddr == 0) {
                arp_cache[i].ipaddr = arp->source_ipaddr;
                memcpy(&arp_cache[i].hwaddr[0], &arp->source_hwaddr[0], 6);
                arp_cache[i].timer = CACHETIME;
                DBGMSG("ARP : New cache entry added ! " );
                break;
            }
        }

        // If no blank entries in arp cache then sort cache
        // to find oldest entry and replace it
        if (i == CACHESIZE) {
            // Oldest entry is the one with lowest timer value
            minimum = 0xFFFF;
            for (i = 0; i < CACHESIZE; i++) {
                if (arp_cache[i].timer < minimum) {
                    minimum = arp_cache[i].timer;
                    oldest = i;
                }
            }

            // "oldest" is now index of oldest entry, so replace it
            arp_cache[oldest].ipaddr = arp->source_ipaddr;
            memcpy(&arp_cache[oldest].hwaddr[0], &arp->source_hwaddr[0], 6);
            arp_cache[oldest].timer = CACHETIME;

            DBGMSG("ARP : CacheFull,ReplacedOldest" );
        }
    }

    // If we are waiting for an arp response and the arp response
    // that just came in is addressed to me and is from the host
    // we are waiting for, then send the message-in-waiting
    if (arp->message_type == ARP_RESPONSE) {
        if ((waiting_for_arp) && (wait1.ipaddr == arp->source_ipaddr)) {
            waiting_for_arp = FALSE;
            DBGMSG("ARP : Resend IP packet!       " );
            ip_send(wait1.buf, wait1.ipaddr, wait1.proto_id, wait1.len);
        }
    }
    else if (arp->message_type == ARP_REQUEST) {
        // Send ARP response
        DBGMSG("ARP : Sending response !      " );
        arp_send(arp->source_hwaddr, arp->source_ipaddr, ARP_RESPONSE);
    }
}
